﻿namespace WriterStars.Data
{
    using System;
    using System.Collections.Generic;
    using WriterStars.Data.Repository;
    using WriterStars.Model;

    public class WriterStarsData : IWriterStarsData
    {
        private IDictionary<Type, object> repositories;
        private IDictionary<Type, Type> repositoryType;

        private IWriterStarsDbContext context;

        public WriterStarsData()
            :this(new WriterStarsDbContext())
        {
        }

        public WriterStarsData(IWriterStarsDbContext db)
        {
            this.repositories = new Dictionary<Type, object>();
            this.repositoryType = new Dictionary<Type, Type>();
            this.context = db;

            this.SetRepositoryTypes();
        }

        public IBooksRepository Books
        {
            get { return (IBooksRepository)this.GetRepository<Book>(); }
        }

        public ILikesRepository Likes
        {
            get { return (ILikesRepository)this.GetRepository<Like>(); }
        }

        public IOrderRepository Orders
        {
            get { return (IOrderRepository)this.GetRepository<Order>(); }
        }

        public IAuthorsRepository BlogBooks
        {
            get { return (IAuthorsRepository)this.GetRepository<Author>(); }
        }

        public IContestRepository Contests
        {
            get { return (IContestRepository)this.GetRepository<Contest>(); }
        }
        

        public ICategoryRepository Categories
        {
            get { return (ICategoryRepository)this.GetRepository<Category>(); }
        }

        public IUserCategoryRepository UserCategorys
        {
            get { return (IUserCategoryRepository)this.GetRepository<UserCategory>(); }
        }

        public ISubscriberRepository Subscribers
        {
            get { return (ISubscriberRepository)this.GetRepository<Subscriber>(); }
        }
        
        // TODO: Rework - Make the User class inherit IEntity
        public IUserRepository Users
        {
            get
            {
                var type = typeof(User);

                if (!this.repositories.ContainsKey(type))
                {
                    this.repositories.Add(type, new UserRepository());
                }

                return (IUserRepository)this.repositories[type];
            }
        }

        public INotificationRepository Notifications
        {
            get { return (INotificationRepository)this.GetRepository<Notification>(); }
        }

        private IRepository<T> GetRepository<T>() where T : class, IEntity
        {
            var typeOfModel = typeof(T);
            if (!this.repositories.ContainsKey(typeOfModel))
            {
                try
                {
                    var repositoryType = this.repositoryType[typeOfModel];
                    this.repositories.Add(typeOfModel, Activator.CreateInstance(repositoryType, this.context));
                }
                catch (KeyNotFoundException)
                {
                    throw new KeyNotFoundException(typeOfModel.ToString());
                }
            }

            return (IRepository<T>)this.repositories[typeOfModel];
        }

        // TODO: Generate this list with Reflection
        private void SetRepositoryTypes()
        {
            this.repositoryType.Add(typeof(User), typeof(UserRepository));
            this.repositoryType.Add(typeof(Book), typeof(BooksRepository));
            this.repositoryType.Add(typeof(Author), typeof(AuthorRepository));
            this.repositoryType.Add(typeof(Contest), typeof(ContestRepository));
            this.repositoryType.Add(typeof(Order), typeof(OrderRepository));
            this.repositoryType.Add(typeof(Like), typeof(LikeRepository));
            this.repositoryType.Add(typeof(Subscriber), typeof(SubscriberRepository));
            this.repositoryType.Add(typeof(Category), typeof(CategoryRepository));
            this.repositoryType.Add(typeof(UserCategory), typeof(UserCategoryRepository));
            this.repositoryType.Add(typeof(Notification), typeof(NotificationRepository));

        }
    }
}
